package com.angel.architecture.flex.serialization;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.angel.architecture.flex.hibernate.HibernateUtils;
import com.angel.architecture.flex.serialization.converter.DatePropertyConverter;
import com.angel.architecture.flex.serialization.converter.HibernateCollectionConverter;
import com.angel.architecture.flex.serialization.converter.PropertyConverter;
import com.angel.architecture.flex.serialization.converter.SimpleValueConverter;
import com.angel.architecture.flex.serialization.property.PropertyAccessor;

@Component
public class DefaultSerializationStrategy implements SerializationStrategy, InitializingBean {
    @Autowired
    private SessionFactory sessionFactory;
    private List<PropertyConverter> globalConverters = new ArrayList<PropertyConverter>();
    private List<SimpleValueConverter> parameterConverters = new ArrayList<SimpleValueConverter>();
    private List<SimpleValueConverter> returnConverters = new ArrayList<SimpleValueConverter>();

    public Object serializeProperty(Object propertyOwner, PropertyAccessor propertyAccessor) {
        SerializationStatus status = new SerializationStatus();
        for (PropertyConverter converter : globalConverters) {
            Object result = converter.serializeProperty(propertyOwner, propertyAccessor, status);
            if (status.isComplete()) {
                return result;
            }
        }
        return propertyAccessor.get(propertyOwner);
    }

    public Object deserializeProperty(Object propertyOwner, PropertyAccessor propertyAccessor, Object remoteValue) {
        SerializationStatus status = new SerializationStatus();
        for (PropertyConverter converter : globalConverters) {
            Object result = converter.deserializeProperty(propertyOwner, propertyAccessor, remoteValue, status);
            if (status.isComplete()) {
                return result;
            }
        }
        return remoteValue;
    }

    private Object doConversion(Object originalValue, List<SimpleValueConverter> converters, boolean isParameter) {
        Object currentValue = originalValue;
        for (SimpleValueConverter converter : converters) {
            if (isParameter) {
                currentValue = converter.deserializeParameter(currentValue);
            } else
                currentValue = converter.serializeReturnValue(currentValue);
        }
        return currentValue;
    }

    public Object convertParameter(Object remoteParameter) {
        return doConversion(remoteParameter, this.parameterConverters, true);
    }

    public Object convertReturnValue(Object localReturnValue) {
        return doConversion(localReturnValue, this.returnConverters, false);
    }

    public String getClassName(Object instance) {
        return HibernateUtils.getActualClass(instance).getName();
    }

    public void afterPropertiesSet() throws Exception {
        DatePropertyConverter dateConverter = new DatePropertyConverter();
        globalConverters.add(new HibernateCollectionConverter(sessionFactory));
        globalConverters.add(dateConverter);
        parameterConverters.add(dateConverter);
        returnConverters.add(dateConverter);
    }
}
